extern crate rayon;
use rayon::prelude::*;
use std::borrow::Borrow;
use std::sync::{Arc, Condvar, Mutex};
use std::time::Duration;
// Multiple Producers Single Consumer
use std::sync::mpsc;
use std::thread;

#[test]
fn test_channel_basic() {
    let (tx, rx): (mpsc::Sender<i32>, mpsc::Receiver<i32>) = mpsc::channel();

    thread::spawn(move || {
        tx.send(1).unwrap();
    });

    println!("recv {:?}", rx.recv().unwrap());
}

#[derive(Debug)]
struct Student {
    id: u32,
}

#[test]
fn test_channel_complex() {
    // Rc can't send between threads
    let (tx, rx): (mpsc::Sender<Arc<Student>>, mpsc::Receiver<Arc<Student>>) = mpsc::channel();

    thread::spawn(move || {
        tx.send(Arc::new(Student { id: 1 })).unwrap();
    });

    println!("recv Student {:?}", rx.recv().unwrap());
}

#[test]
fn test_multiple_communicate() {
    let num_threads = 10;

    let (tx, rx) = mpsc::channel();

    for i in 0..num_threads {
        let tx_clone = tx.clone();
        thread::spawn(move || tx_clone.send(i).unwrap());
    }
    for _ in 0..num_threads {
        println!("recv {:?}", rx.recv().unwrap());
    }
}

#[test]
fn test_sync_channel() {
    let (tx, rx): (mpsc::SyncSender<i32>, mpsc::Receiver<i32>) = mpsc::sync_channel(0);

    thread::spawn(move || {
        println!(" {:?}", "before send");
        tx.send(1).unwrap();
        println!(" {:?}", "after send");
    });

    thread::sleep(Duration::from_millis(500));
    println!(" {:?}", "before recv");
    println!("recv {:?}", rx.recv().unwrap());
    println!(" {:?}", "after recv");
}

static VAR: i32 = 5;

#[test]
fn test_memory_share_single() {
    let t = thread::spawn(|| {
        println!("VAR {:?} in thread", VAR);
    });

    t.join().unwrap();
    println!("VAR {:?} in main", VAR);
}

static mut VAR2: i32 = 10;
#[test]
fn test_memory_share_write() {
    let t = thread::spawn(|| unsafe {
        VAR2 = 11;
        println!("VAR {:?}", VAR2);
    });

    t.join().unwrap();
    unsafe {
        println!("VAR {:?}", VAR2);
    }
}

#[test]
fn test_memory_share_box_write() {
    let obj = Arc::new(1);
    let share_obj = obj.clone();
    let t = thread::spawn(move || {
        println!(
            "share obj in new thread: {}, address: {:p}",
            share_obj, &*share_obj
        );
    });

    t.join().unwrap();
    println!("share obj in main thread: {}, address: {:p}", obj, &*obj);
}

#[test]
fn test_synchronize() {
    let pair = Arc::new((Mutex::new(false), Condvar::new()));
    let pair2 = pair.clone();

    thread::spawn(move || {
        let (lock, cond) = pair2.borrow();
        let mut started = lock.lock().unwrap();
        *started = true;
        println!(" {:?}", "ready...");
        cond.notify_one();
    });

    let (lock, cond) = &*pair;
    let mut started = lock.lock().unwrap();
    while !*started {
        println!(" {:?}", "before wait");
        started = cond.wait(started).unwrap();
        println!(" {:?}", "after wait");
    }
}

#[test]
fn test_parallel() {
    let mut colors = [
        -20.0f32, 0.0, 20.0, 40.0, 80.0, 100.0, 150.0, 180.0, 200.0, 250.0, 300.0,
    ];
    println!("original:    {:?}", &colors);

    colors.par_iter_mut().for_each(|c| {
        let correct = if *c < 0.0 {
            0.0
        } else if *c > 255.0 {
            255.0
        } else {
            *c
        };
        *c = correct / 255.0;
    });

    println!("correct: {:?}", colors);
}

#[test]
fn test_thread_scope() {
    let data = Arc::new(Mutex::new(vec![1, 2, 3]));

    let (tx, rx) = mpsc::channel();
    for i in 0..=2 {
        let data_clone = data.clone();
        let tx_clone = tx.clone();
        thread::spawn(move || {
            let mut arr = data_clone.lock().unwrap();
            arr[i] = i + 10;
            tx_clone.send(()).unwrap();
        });
    }

    for _ in 0..=2 {
        rx.recv().unwrap();
    }
    println!("{:?}", data);
}
